home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 11 / Cream of the Crop 11-2.iso / extra / backgmmn.zip / BACKGAMM.C < prev    next >
C/C++ Source or Header  |  1995-12-27  |  17KB  |  530 lines

  1. /************************************************************************/
  2. /*                                    */
  3. /*  Hoser BackGammon version 1.0                    */
  4. /*                                    */
  5. /*    Robert Pfister                            */
  6. /*                                    */
  7. /*    Rfd#3 Box 2340        home:(207)-873-3520            */
  8. /*    Waterville, Maine 04901                        */
  9. /*                                    */
  10. /*      Pfister_rob%dneast@dec.decwrl                    */
  11. /*                                    */
  12. /*                                    */
  13. /*  Copyright  June,1987                        */
  14. /*                                    */
  15. /*  This program will play a game of backgammon at the novice level    */
  16. /*                                    */
  17. /*  The code is in 4 parts...                        */
  18. /*   /                                    */
  19. /* \/   1) back.c     - main driver                    */
  20. /*    2) eval.c     - evaluation of moves                */
  21. /*    3) backscn.c  - screen stuff..                    */
  22. /*    4) backmenu.c - menu stuff, help text, and "decoder"         */
  23. /*                                    */
  24. /* this was compiled under Manx 3.20a, using long integers        */
  25. /*                                    */
  26. /************************************************************************/
  27. /* Contains:                                */
  28. /*    ShowStandings    main        Restart        StoreMove    */
  29. /*    RecallMove    Won        UserMove    Roll        */
  30. /*    GameOver    stats        NewGame        valid        */
  31. /*    update                                */
  32. /* most recent modification    11/ 8/93    E.M.Greene        */
  33. /* most recent modification     2/14/94    E.M.Greene        */
  34. /* most recent modification     3/ 9/95    E.M.Greene        */
  35. /* most recent modification     7/ 1/95    E.M.Greene        */
  36.  
  37. /* Revisions, corrections, and improvements include:
  38. 1. Corrected errors in provided source code:
  39.    a. InitArea() call in Gsetup had incorrect value for number of points
  40.       thereby resulting in no pieces being displayed.
  41.    b. TextScreen() failed to perform CloseDevice() upon completion
  42.       thereby resulting in strange phenomena after being called
  43.    c. stats() failed to display anything if program initiated from
  44.       Workbench.  Revised to call TextScreen.
  45.    d. Bug in eval() resulted in poor performance.  Correction made the
  46.       computer a much more vigorous opponent -- it'll now eat your lunch
  47.       a goodly percentage of the time.
  48.    e. Numerous grammatical and spelling errors corrected, mainly "peice".
  49.    f. Revised the process of displaying numbers on pieces when more than
  50.       PerSpike pieces are on one point so that counts greater than nine
  51.       are shown correctly.
  52.    g. Corrected error in StoreMove so that "stack" overflow is properly
  53.       detected.
  54.  
  55.    h. Added code to reset StackPos at start of new game.  Otherwise, if
  56.       you play long enough, the stack would overflow and the bug of
  57.       preceding item will result in a flameout.
  58.    i. Eliminated Gerror from BackScreen and replaced with Alerts.  Gerror
  59.       was trying to display error message about screen or window opening
  60.       failures on a screen or window which hadn't opened!
  61.  
  62. 2. Revisions:
  63.    a. Eliminated dead code and variables.  Variable names were revised in
  64.       a number of places to be more meaningful.  Reformatted source and
  65.       added comments to make the source more readable.
  66.    b. Merged IntuiMessage processing into one routine and revised to look
  67.       for all messages currently queued.  Menu operations are now much
  68.       more effective.
  69.    c. Revised the dice "throw" generation to reduce the instances of the
  70.       same values being repeatedly generated.
  71.    d. Reduced declared sizes for AreaBuf and StackSize to something more
  72.       reasonable.
  73.    e. Replaced the calls to the lightly-documented function RangeRand
  74.       with calls to Scott McMahan's implementation of the algorithm of
  75.       Pierre L'Ecuyer [L'Ecuyer, P. Efficient and Portable Combined Random
  76.       Number Generators. Commun. ACM 31, 6 (June, 1988) 742].
  77.    f. Replaced the routine CopyBoard in BackEval with a macro invoking
  78.       memcpy().
  79. 3. Enhancements:
  80.    a. Array point[] was added to eliminate need to constantly compute
  81.       board positions which are constants.
  82.    b. Loop count in BlinkPiece() was reduced to expedite moves.
  83.    c. Only one pair of dice are displayed thereby eliminating confusion
  84.       about which pair of numbers human is to play.
  85.    d. Revised color specification such that ColorRequestor can potentially
  86.       be added.
  87.    e. Added readout of relative standings of players.
  88.    f. Recompiled with Lattice C resulting in more randomness in dice throws.
  89.       [SAS Institute had ought to know how to produce a random number generator
  90.       since they are in the statistical analysis business!]
  91.    g. Revised game statistics to include number of points rolled but not
  92.       used ("wasted").  Also, now show average roll to one decimal place.
  93.    h. Revised as required to compile cleanly with SAS/C 6.3
  94.    i. Added "cost" of coming off bar to "wasted" count    (11/16/94)
  95.    j. Added counting of total rolls and display (2/14/94).
  96.    k. Added total games and effective average roll to data display (12/27/95)
  97. */
  98. #include <proto/exec.h>
  99. #include <proto/graphics.h>
  100. #include <proto/intuition.h>
  101. #include <libraries/dos.h>
  102. #include "back.h"
  103. #include <string.h>
  104. #include <stdlib.h>
  105. #include <stdio.h>
  106. #include <stddef.h>
  107. #include <math.h>
  108.  
  109. #define StackSize    100
  110.  
  111. extern int        AvgPos;
  112. extern int        Moves;
  113. extern int        minY;
  114. extern struct RastPort    *rp;
  115. extern struct Screen    *screen;
  116.  
  117. you_me_rec        you, me;
  118. USHORT            total_games = 0;
  119. static signed char
  120.     BoardPos[StackSize][26],/* stack of the previous positions    */
  121.     DicePos[StackSize][4],    /* stack of the dice rolls        */
  122.     StackPos,        /* how many moves back are we at?    */
  123.     MoveList[4],        /* stack of this turn's moves        */
  124.     CurMove = 0;        /* place in the list            */
  125. signed char    Turn,        /* whose turn is it?            */
  126.         board[26],    /* describes the board position        */
  127.                 /* 0 and 25 are the bar            */
  128.         Dice[4];    /* describes the 2 or 4 dice availible    */
  129. const char    version[] = "$VER: Backgammon 2.2 (95/12/27)";
  130.  
  131. void ShowStandings(void)
  132. {
  133.   char    s[6];
  134.   int    Msum=0,  Ysum=0, i;
  135.  
  136.   for(i = 0; i <= 24; i++)
  137.     if (board[i] > 0)
  138.       Msum += (25 - i) * board[i];
  139.   for(i = 1; i <= 25; i++)
  140.     if (board[i] < 0)
  141.       Ysum -= i * board[i];
  142.   /* write box scores at top of window    */
  143.   SetAPen(rp, tm1_color);
  144.   RectFill(rp, 400L, minY-3-screen->Font->ta_YSize, 520, minY-2);
  145.   SetAPen(rp,dice_color);
  146.   sprintf(s,"%4d", Msum);
  147.   if (board[0] != 0)
  148.     strcat(s,"+");
  149.   Move(rp, 400L, (long)minY - 6);
  150.   Text(rp,s,strlen(s));
  151.   sprintf(s,"%4d", Ysum);
  152.   if (board[25] != 0)
  153.     strcat(s,"+");
  154.   Move(rp, 460L, (long)minY - 6);
  155.   Text(rp, s, strlen(s));
  156. }
  157.  
  158. double random(void);
  159.  
  160. unsigned short real_rand(void)
  161. {
  162.   return (unsigned short)(random() * 6.0);
  163. }
  164.  
  165. int Roll(BYTE d[4])            /* roll the dice  (4 results)    */
  166. {
  167.   int    n;
  168.  
  169.   d[2] = d[3] = 0;
  170.   for (n = (real_rand() >> 5) & 7; n >= 0; n--)
  171.     d[0] += real_rand();
  172.   d[1] = (abs(d[0]) % 6) + 1;        /* RangeRand(6L)+1; */
  173.   for (n = (real_rand() >> 7) & 7; n >= 0; n--)
  174.     d[0] += real_rand();
  175.   d[0] = (abs(d[0]) % 6) + 1;        /* RangeRand(6L)+1; */
  176.   if (d[0]==d[1]) {            /* if rolled doubles        */
  177.     d[2] = d[3] = d[1];
  178.     return 1;
  179.   }
  180.   else
  181.     return 0;
  182. }
  183.  
  184. int NewGame(signed char board[26], signed char Dice[4])
  185. {
  186.   int i;
  187.  
  188.   StackPos = 0;
  189.   memset(board, 0, sizeof_board);    /* initialize the board        */
  190.  
  191.   board[ 1]= 2;        board[24]=-2;
  192.   board[12]= 5;        board[ 6]=-5;
  193.   board[17]= 3;        board[ 8]=-3;
  194.   board[19]= 5;        board[13]=-5;
  195.   memset(&you, 0, offsetof(you_me_rec, total.roll));
  196.   memset(&me,  0, offsetof(you_me_rec, total.roll));
  197.   AvgPos = 0;
  198.  
  199.   for (i = 0; i <= 25;i ++)
  200.     PutSpike(i, board[i]);
  201.  
  202.   while (Roll(Dice));            /* until no doubles        */
  203.   if (Dice[0]>Dice[1])            /* see who goes first        */
  204.     return(Uside);
  205.   else
  206.     return(Cside);
  207. }
  208.  
  209. #include "intuition/intuition.h"
  210. extern struct Window    *w;
  211.  
  212. int UserMove(BYTE board[26], BYTE Dice[4], int side)
  213. {
  214.   BYTE            Pick = 0, Valid = 0;  /* flags */
  215.   int            i,x,y,
  216.             from_point, to_point;
  217.   struct IntuiMessage    *message;
  218.   unsigned long        Class, msg_flag = 1L << w->UserPort->mp_SigBit;
  219.   unsigned short    Code;
  220.  
  221.   while (Valid == 0) {
  222.     Wait(msg_flag);
  223.     while (message=(struct IntuiMessage *) GetMsg(w->UserPort)) {
  224.       Code = message->Code;
  225.       Class = message->Class;
  226.       x = message->MouseX;
  227.       y = message->MouseY;
  228.       ReplyMsg((struct Message *)message);
  229.       if (Class == MENUPICK) {
  230.     if (Code != MENUNULL)
  231.       Valid=DoMenu((MENUNUM(Code)*100)+(ITEMNUM(Code)*10)+SUBNUM(Code));
  232.       }
  233.       /* check if mouse pressed..... */
  234.       else if ((Class == MOUSEBUTTONS) && (Code == SELECTDOWN)) {
  235.     if ((to_point = decode(x,y)) == -1) {    /* clicked dice        */
  236.       you.wasted += Dice[0] + Dice[1] + Dice[2] + Dice[3];
  237.       Dice[0] = Dice[1] = Dice[2] = Dice[3] = 0;
  238.       Valid = 1;
  239.       you.incomplete++;
  240.     }
  241.     else
  242.       if (Pick == 0) {        /* first click            */
  243.         if (board[to_point] * side <= 0)
  244.           DoMenuStrip("No piece there!!");
  245.         else {
  246.           Pick = 1;
  247.           from_point = to_point;
  248.         }
  249.         break;
  250.       }
  251.  
  252.       else if (Pick == 1) {        /* second click            */
  253.         Pick = 0;
  254.         for (i = 0; (Valid == 0) && (i < 4); i++)
  255.           if ((Dice[i] != 0) &&
  256.          (valid(board, from_point, to_point, Dice[i]))) {
  257.         if (from_point == 25)
  258.           you.wasted += 25 - to_point;
  259.         if (from_point == to_point)
  260.           you.wasted += (Dice[i] - from_point);
  261.         BlinkPiece(board, from_point);
  262.         update(board, from_point, to_point, side);
  263.         PutSpike(from_point , board[from_point]);
  264.         BlinkPiece(board, to_point);
  265.         PutSpike(0 , board[ 0]);
  266.         PutSpike(25, board[25]);
  267.         Valid = 1;
  268.         Dice[i] = 0;
  269.           }
  270.         UnDoMenuStrip();
  271.       }    /* end Pick == 1 */
  272.         /* end else decode */
  273.     if (Valid == 0)
  274.       DoMenuStrip("Not Valid move");
  275.       };    /* end if mousebutton */
  276.     }        /* end while message */
  277.   }        /* end while Valid */
  278.   return (Valid != -1);
  279. } /* end UserMove */
  280.  
  281. void StoreMove(void)
  282. {
  283.   if (StackPos < StackSize - 1) {    /* can only store that many    */
  284.     memcpy(&BoardPos[StackPos][0], board, sizeof board);
  285.     memcpy(&DicePos[StackPos][0], Dice, sizeof Dice);
  286.     StackPos++;
  287.   }
  288.   /* printf("STORE: stack is at %d \n",StackPos); */
  289. }
  290.  
  291. void stats(void)
  292. {
  293.   char *stat_data[] = {
  294.     " Game Statistics",
  295.     "\n",
  296.     " Number of games: xx\n  ",
  297.     "\n",
  298.     " Number of moves xx;  Avg positions evaluated xxxx \n",
  299.     "\n",
  300.     " Avg. roll   : User  xx.x Computer  xx.x \n  ",
  301.     " Effect. avg.: User  xx.x Computer  xx.x \n  ",
  302.     " Doubles     : User  xx   Computer  xx \n    ",
  303.     " Inc. moves  : User  xx   Computer  xx \n    ",
  304.     " Turns on bar: User  xx   Computer  xx \n    ",
  305.     " Wasted      : User  xx   Computer  xx \n    ",
  306.     "\n",
  307.     "    [click in window to continue]\n         "};
  308.  
  309.   if ((you.moves != 0) && (me.moves != 0)) {
  310.     sprintf(stat_data[2], "Number of games: %d\n", ++total_games);
  311.     sprintf(stat_data[4]," Number of moves %d;"
  312.     "  Avg positions evaluated %d\n", me.moves, AvgPos / me.moves);
  313.     sprintf(stat_data[6]," Avg roll    : User %5.1f Computer %5.1f \n",
  314.     (float)you.dice_total / you.moves, (float)me.dice_total / me.moves);
  315.     sprintf(stat_data[7]," Effect. avg.: User %5.1f Computer %5.1f \n",
  316.     (float)(you.dice_total - you.wasted) / you.moves,
  317.     (float)(me.dice_total - me.wasted) / me.moves);
  318.     sprintf(stat_data[8]," Doubles     : User  %2d   Computer  %2d \n",
  319.     you.doubles, me.doubles);
  320.     sprintf(stat_data[9]," Inc. moves  : User  %2d   Computer  %2d \n",
  321.     you.incomplete, me.incomplete);
  322.     sprintf(stat_data[10]," Turns on bar: User  %2d   Computer  %2d\n",
  323.     you.on_bar, me.on_bar);
  324.     sprintf(stat_data[11]," Wasted      : User %3d   Computer %3d\n",
  325.     you.wasted, me.wasted);
  326.     TextScreen(stat_data, 14);
  327.     you.total.roll += you.dice_total;
  328.     me.total.roll += me.dice_total;
  329.     you.total.average_roll += (float)you.dice_total / you.moves;
  330.     me.total.average_roll += (float)me.dice_total / me.moves;
  331.     you.total.incomplete += you.incomplete;
  332.     me.total.incomplete += me.incomplete;
  333.     you.total.wasted += you.wasted;
  334.     me.total.wasted += me.wasted;
  335.   }
  336. }
  337.  
  338. void GameOver(BOOL show_stats)
  339. {
  340.   if (show_stats)
  341.     stats();
  342.   finit();
  343.   exit(0);
  344. }
  345.  
  346. int Won(BYTE board[26], int side)
  347. {
  348.   int i;
  349.  
  350.  if (side == 1) {
  351.    for(i = 0;i <= 25;i++)
  352.      if (board[i] > 0)
  353.        return 0;
  354.  }
  355.  else
  356.    for(i = 0;i <= 25; i++) {
  357.      if (board[i] < 0)
  358.        return 0;
  359.  }
  360.  return(1);
  361. }
  362.  
  363. void setseed(void);
  364.  
  365. main() /* main procedure */
  366. {
  367.   Gsetup();
  368.   memset(&you, 0, sizeof you);
  369.   memset(&me,  0, sizeof me);
  370.   setseed();
  371.   Turn = NewGame(board,Dice);
  372.   for (;;) { /* go until game is over */
  373.     if (Turn == Uside)
  374.       DoMenuStrip("Your turn");
  375.     else
  376.       DoMenuStrip("My turn");
  377.     ShowDice(Dice,Turn);
  378.     ShowStandings();
  379.     if (Turn == Uside) {
  380.       StoreMove(); /* store this board position */
  381.        /* user stats */
  382.       if (Dice[2] > 0)
  383.     you.doubles++;
  384.       you.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
  385.       you.moves++;
  386.       if (board[25] < 0)
  387.     you.on_bar++;
  388.       /* do the Move */
  389.       do
  390.         if (UserMove(board,Dice,Turn) == 0)
  391.       GameOver(TRUE); /* escape */
  392.       while ((Turn == Uside) && (Won(board,Turn) == 0) &&
  393.          (Dice[0] + Dice[1] + Dice[2] + Dice[3]!=0));
  394.       you.wasted += (Dice[0] + Dice[1] + Dice[2] + Dice[3]);
  395.     }
  396.  
  397.     else { /* turn == Cside */
  398.       /* computer stats */
  399.       if (Dice[2] > 0)
  400.     me.doubles++;
  401.       me.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
  402.       me.moves++;
  403.       if (board[0] > 0)
  404.     me.on_bar++;
  405.       GenerateMoves(board,Dice);    /* do move            */
  406.       DoMove(board,Dice);
  407.     }
  408.     Turn = -Turn;
  409.     Roll(Dice);
  410.     if (Turn == 0)            /* signal to restart the game    */
  411.       Turn = NewGame(board,Dice);
  412.     if (Won(board,Uside)) {        /* did user win yet?        */
  413.       ShowStandings();
  414.       stats();
  415.       if (requestor("I lose.  Play again?","Yes","No"))
  416.     GameOver(FALSE);
  417.       else
  418.     Turn = NewGame(board,Dice);
  419.     }
  420.     if (Won(board,Cside)) {        /* did computer win yet?    */
  421.       ShowStandings();
  422.       stats();
  423.       if (requestor("Hee hee.  I Win.  Play Again?","Yes","No"))
  424.     GameOver(FALSE);
  425.       else
  426.     Turn = NewGame(board,Dice);
  427.     }
  428.   } /* end do forever */
  429. } /* end of main */
  430.  
  431. void Restart(void)
  432. {
  433.  Turn = 0;
  434. }
  435.  
  436. void RecallMove(int how)
  437. {
  438.   int i;
  439.  
  440.   StackPos = StackPos-1 + how;
  441.   if (StackPos < 0)
  442.     StackPos = 0;
  443.   for (i = 0; i < 26; i++)
  444.     if (board[i] != BoardPos[StackPos][i])  { /* only refresh stuff changed */
  445.       board[i] = BoardPos[StackPos][i];
  446.       PutSpike(i,board[i]);
  447.     }
  448.   for (i = 0; i < 4; i++)
  449.     Dice[i] = DicePos[StackPos][i];
  450.   ShowDice(Dice,Uside);
  451.   StackPos++;
  452.   /* printf("RECALL: stack is at %d \n",StackPos);  */
  453. }
  454.  
  455. int valid(BYTE board[26], int from_point, int to_point, int die_value)
  456. {            /* is the move valid for the board and one die?    */
  457.   BYTE    sign, i, All_In,
  458.     *bp;
  459.  
  460.   All_In = -1;
  461.   if (Turn == Cside) {        /* computer's move            */
  462.     if ((board[0] != 0) && (from_point != 0))
  463.       return(0);        /* man on bar and didn't select bar    */
  464.     sign = Cside;
  465.     /* see if all pieces are in home base */
  466.     bp = &board[1];
  467.     for(i = 1; i <= 24; i++)
  468.       if (*(bp++) > 0) {    /* find man fartherest from home    */
  469.     All_In = i;
  470.         break;
  471.       }
  472.     /* check if can take it off */
  473.     if (to_point > 24) {        /* if in home board        */
  474.       if ((All_In > 18) &&        /*   if all men in home board    */
  475.           ((from_point == (25 - die_value)) ||
  476.        ((25 - die_value) < All_In) && (from_point == All_In)))
  477.         return(1);
  478.       else
  479.         return(0);
  480.     }
  481.   }
  482.  
  483.   else {            /* human's move                */
  484.     if ((board[25] != 0) && (from_point != 25))
  485.       return(0);        /* man on bar and didn't select bar    */
  486.     sign = Uside;
  487.     /* see if all pieces are in home base */
  488.     bp = &board[25];
  489.     for(i = 24; i >= 1; i--)
  490.       if (*(--bp) < 0) {    /* find man fartherest from home    */
  491.     All_In = i;
  492.         break;
  493.       }
  494.     /* see if can take off */
  495.     if (to_point == from_point) {    /* double-clicked?            */
  496.       if ((All_In < 7) &&        /*   all pieces in home board        */
  497.       ((from_point == die_value) ||
  498.        ((die_value > All_In) && (from_point == All_In))))
  499.     return(1);
  500.       else
  501.     return(0);
  502.     }
  503.   }
  504.   if ((to_point-from_point) * sign != die_value)
  505.     return(0);            /* distance to move != die value    */
  506.   if (board[to_point] * sign < -1)
  507.     return(0);            /* can't land at "to" point        */
  508.   return(1);            /* I suppose that's all there is..    */
  509. }
  510.  
  511. int update(BYTE *board, int from_point, int to_point, BYTE sign)
  512. {                /* put a given move onto the board    */
  513.   /* remove the piece from the source spike */
  514.   board[from_point] -= sign;
  515.   /* if taking a piece off board or bar, don't go any further */
  516.   if ((to_point == from_point) || (to_point >= 25))
  517.     return(1);
  518.   /* add a piece to the destination spike and determine if capturing    */
  519.   board[to_point] += sign;
  520.   if (board[to_point] == 0) {        /* must have hit opponent    */
  521.     int bar = 0;
  522.  
  523.     if (sign < 0)
  524.       bar = 25;
  525.     board[to_point] = sign;        /* put us onto point        */
  526.     board[25-bar] -= sign;        /* put opponent on bar        */
  527.   }
  528.   return(0);
  529. }
  530.